#include "StdAfx.h"
#include <fstream>
//#include <functional>
//#include <xutility>
#include "Matrix.h"
#include "codecvt.h"

using namespace std;

bool readWords(const char* file, std::vector<CrossWordItem>& items)
{
	wifstream f(file);
	f.imbue(std::locale(f.getloc(), new ucs2_conversion));
	if (!f)
		return false;
//	f.imbue(locale("rus_rus.1251"));
	while (f)
	{
		CrossWordItem i;
		if (!(f >> i.word))
			return true;
		if (!(f >> i.file))
			return false;
		if (!(f >> i.accent))
			return false;
		items.push_back(i);
	}
	return true;
}

CMatrix::CMatrix()
:m_direction(HORIZ)
,m_mustIntersect(true)
{
}

CMatrix::~CMatrix()
{
}

inline wchar_t CMatrix::at(int row, int col) const
{
	return m_items.at(row).at(col).s;
}

const CMatrix::Item& CMatrix::get(size_t row, size_t col) const
{
	return m_items.at(row).at(col);
}

const CrossWordItem& CMatrix::getHorizWord(int row) const
{
	return m_horizWords[row];
}

const CrossWordItem& CMatrix::getVertWord(int col) const
{
	return m_vertWords[col];
}

void CMatrix::init(int rows)
{
	m_horizWords.resize(rows);
	m_vertWords.resize(rows);
	m_items.resize(rows);
	for (size_t i = 0; i < m_items.size(); ++i)
	{
		m_items[i].resize(rows);
	}
}

bool CMatrix::setAnchor(const CrossWordItem& item)
{
	m_mustIntersect = false;
	bool res = addWord(item);
	m_mustIntersect = true;
	return res;
}

bool CMatrix::canPlace(int row, int col, wchar_t s) const
{
	wchar_t cur = at(row, col);
	return cur == L' ' || cur == s;
}

bool CMatrix::canPlace(int row, int stepX, const std::wstring& s) const
{
	// check there is nothing from left and from top of begining of the word
	if (m_direction == HORIZ)
	{
		if (stepX > 0)
		{
			if (at(row, stepX - 1) != ' ')
				return false;
		}
	} else {
		if (row > 0)
		{
			if (at(row -1, stepX) != ' ')
				return false;
		}
	}

	// check the word itself
	bool intersects = false;
	for (size_t i = 0 ; i < s.length(); ++i)
	{
		if (m_direction == HORIZ)
		{
			if (!canPlace(row, stepX + i, s[i]))
				return false;
			// check there are no neighbors
			if (at(row, stepX + i) == s[i])
			{
				intersects = true;
				continue;
			}
			if (row - 1 >= 0 && at(row - 1, stepX + i) != ' ')
				return false;
			if (row + 1 < (int) m_items.size() && at(row + 1, stepX + i) != ' ')
				return false;
		} else {
			if (!canPlace(row + i, stepX, s[i]))
				return false;
			if (at(row + i, stepX) == s[i])
			{
				intersects = true;
				continue;
			}
			if (stepX - 1 >= 0 && at(row + i, stepX - 1) != ' ')
				return false;
			if (stepX + 1 < (int) m_items.size() && at(row + i, stepX + 1) != ' ')
				return false;
		}
	}
	if (m_direction == HORIZ)
	{
		if (stepX + s.length() < m_items.size())
		{
			if (at(row, stepX + s.length()) != ' ')
				return false;
		}
	} else {
		 if (row + s.length() < m_items.size())
		 {
			 if (at(row + s.length(), stepX) != ' ')
				 return false;
		 }
	}
	if (m_mustIntersect)
		return intersects;
	return true;
}

std::pair<int, int> CMatrix::findRow(int stepY, int stepX, const CrossWordItem& item) const
{
	int row = m_items.size() / 2 + stepY;
//	cout << "try hor " << item.word << " at " << row << ", " << stepX << endl;
	if (row < 0 || row >= (int)m_items.size())
	{
		if (stepX == 0 && 1 + item.word.length() <= m_items.size())
			return findRow(0, 1, item);
		return make_pair(-1, -1);
	}

	if (stepY < 0)
		stepY *= -1;
	else
		stepY = -stepY - 2;
	if (!canPlace(row, stepX, item.word))
		return findRow(stepY, stepX, item);
	return make_pair(row, stepX);
}

void CMatrix::putAtRow(int row, int stepX, const CrossWordItem& item)
{
	m_horizWords[row] = item;
	m_horizWords[row].pos = stepX;
	for (size_t i = 0 ; i < item.word.length(); ++i)
	{
		m_items[row][i + stepX].s = item.word[i];
		if (i == item.accent -  1)
			m_items[row][i + stepX].accent = true;
	}
}

std::pair<int, int> CMatrix::findCol(int stepY, int stepX, const CrossWordItem& item) const
{
	int col = m_items.size() / 2 + stepX;
//	cout << "try vert " << item.word << " at " << col << ", " << stepY << endl;
	if (col < 0 || col >= (int)m_items.size())
	{
		if (stepY == 0 &&  1 + item.word.length() <= m_items.size())
			return findCol(1, 0, item);
		return make_pair(-1, -1);
	}

	if (stepX < 0)
		stepX *= -1;
	else
		stepX = -stepX - 1;
	if (!canPlace(stepY, col, item.word))
		return findCol(stepY, stepX, item);
	return make_pair(col, stepY);
}

void CMatrix::putAtCol(int col, int stepY, const CrossWordItem& item)
{
	m_vertWords[col] = item;
	m_vertWords[col].pos = stepY;

	for (size_t i = 0 ; i < item.word.length(); ++i)
	{
		m_items[i + stepY][col].s = item.word[i];
		if (i == item.accent - 1)
			m_items[i + stepY][col].accent = true;
	}
}

bool CMatrix::addWord(const CrossWordItem& item)
{
	if (item.word.length() > m_items.size())
		return false;
	m_direction = HORIZ;
	{
		pair<int, int> res = findRow(0, 0, item);
		if (res.first != -1)
		{
			putAtRow(res.first, res.second, item);
			return true;
		}
	}
	m_direction = VERT;
	pair<int, int> res = findCol(0, 0, item);
	if (res.first != -1)
	{
		putAtCol(res.first, res.second, item);
	}
	return res.first != -1;
}

void CMatrix::dump()
{
#ifdef WIN32
	wcout << "  ";
	for (size_t i = 0; i < m_items.size(); ++i)
		wcout << i;
	wcout << endl;
	for (size_t i = 0; i < m_items.size(); ++i)
	{
		wcout << i << L' ';
		for (size_t j = 0; j < m_items[i].size(); ++j)
		{
			wcout << m_items[i][j].s;
		}
		wcout << endl;
	}
#endif
}
